{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5.2 PyTorch的nn模块"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<torch._C.Generator at 0x11a54d350>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "\n",
    "torch.set_printoptions(edgeitems=2)\n",
    "torch.manual_seed(2020)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([11, 1])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]\n",
    "t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]\n",
    "t_c = torch.tensor(t_c).unsqueeze(1) # <1>\n",
    "t_u = torch.tensor(t_u).unsqueeze(1) # <1>\n",
    "\n",
    "t_u.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([ 2,  7,  8,  9, 10,  0,  1,  4,  6]), tensor([3, 5]))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n_samples = t_u.shape[0]\n",
    "n_val = int(0.2 * n_samples)\n",
    "\n",
    "shuffled_indices = torch.randperm(n_samples)\n",
    "\n",
    "train_indices = shuffled_indices[:-n_val]\n",
    "val_indices = shuffled_indices[-n_val:]\n",
    "\n",
    "train_indices, val_indices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "t_u_train = t_u[train_indices]\n",
    "t_c_train = t_c[train_indices]\n",
    "\n",
    "t_u_val = t_u[val_indices]\n",
    "t_c_val = t_c[val_indices]\n",
    "\n",
    "t_un_train = 0.1 * t_u_train\n",
    "t_un_val = 0.1 * t_u_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[1.2665],\n",
       "        [1.0006]], grad_fn=<AddmmBackward>)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "linear_model = nn.Linear(1, 1) # 参数: input size, output size, bias(默认True)\n",
    "linear_model(t_un_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[1.2665],\n",
       "        [1.0006]], grad_fn=<AddmmBackward>)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "linear_model.forward(t_un_val) # 虽然输出结果是一样的但不应该这样做!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Parameter containing:\n",
       "tensor([[0.0806]], requires_grad=True)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "linear_model.weight"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Parameter containing:\n",
       "tensor([0.6065], requires_grad=True)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "linear_model.bias"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0.6871], grad_fn=<AddBackward0>)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.ones(1)\n",
    "linear_model(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871],\n",
       "        [0.6871]], grad_fn=<AddmmBackward>)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.ones(10, 1)\n",
    "linear_model(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "linear_model = nn.Linear(1, 1)\n",
    "optimizer = optim.SGD(\n",
    "    linear_model.parameters(),\n",
    "    lr=1e-2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<generator object Module.parameters at 0x11abacf10>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "linear_model.parameters()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Parameter containing:\n",
       " tensor([[0.5563]], requires_grad=True), Parameter containing:\n",
       " tensor([-0.0067], requires_grad=True)]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(linear_model.parameters())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def training_loop(n_epochs, optimizer, model, loss_fn, \n",
    "                  t_u_train, t_u_val, t_c_train, t_c_val):\n",
    "    for epoch in range(1, n_epochs + 1):\n",
    "        t_p_train = model(t_un_train)\n",
    "        loss_train = loss_fn(t_p_train, t_c_train)\n",
    "        \n",
    "        t_p_val = model(t_un_val)\n",
    "        loss_val = loss_fn(t_p_val, t_c_val)\n",
    "        \n",
    "        optimizer.zero_grad()\n",
    "        loss_train.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        if epoch == 1 or epoch % 1000 == 0:\n",
    "            print('Epoch %d, Training loss %.4f, Validation loss %.4f' % (\n",
    "                    epoch, float(loss_train), float(loss_val)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1, Training loss 66.0145, Validation loss 239.8421\n",
      "Epoch 1000, Training loss 4.1785, Validation loss 9.3629\n",
      "Epoch 2000, Training loss 3.2060, Validation loss 3.8825\n",
      "Epoch 3000, Training loss 3.1601, Validation loss 3.0151\n",
      "\n",
      "Parameter containing:\n",
      "tensor([[5.0940]], requires_grad=True)\n",
      "Parameter containing:\n",
      "tensor([-16.0012], requires_grad=True)\n"
     ]
    }
   ],
   "source": [
    "linear_model = nn.Linear(1, 1)\n",
    "optimizer = optim.SGD(linear_model.parameters(), lr=1e-2)\n",
    "\n",
    "training_loop(\n",
    "    n_epochs = 3000,\n",
    "    optimizer = optimizer,\n",
    "    model = linear_model,\n",
    "    loss_fn = nn.MSELoss(), # 不再使用自己定义的loss\n",
    "    t_u_train = t_un_train,\n",
    "    t_u_val = t_un_val,\n",
    "    t_c_train = t_c_train,\n",
    "    t_c_val = t_c_val)\n",
    "\n",
    "print()\n",
    "print(linear_model.weight)\n",
    "print(linear_model.bias)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Sequential(\n",
       "  (0): Linear(in_features=1, out_features=13, bias=True)\n",
       "  (1): Tanh()\n",
       "  (2): Linear(in_features=13, out_features=1, bias=True)\n",
       ")"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "seq_model = nn.Sequential(\n",
    "            nn.Linear(1, 13),\n",
    "            nn.Tanh(),\n",
    "            nn.Linear(13, 1))\n",
    "seq_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[torch.Size([13, 1]), torch.Size([13]), torch.Size([1, 13]), torch.Size([1])]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[param.shape for param in seq_model.parameters()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.weight torch.Size([13, 1])\n",
      "0.bias torch.Size([13])\n",
      "2.weight torch.Size([1, 13])\n",
      "2.bias torch.Size([1])\n"
     ]
    }
   ],
   "source": [
    "for name, param in seq_model.named_parameters():\n",
    "    print(name, param.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Sequential(\n",
       "  (hidden_linear): Linear(in_features=1, out_features=8, bias=True)\n",
       "  (hidden_activation): Tanh()\n",
       "  (output_linear): Linear(in_features=8, out_features=1, bias=True)\n",
       ")"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from collections import OrderedDict\n",
    "\n",
    "seq_model = nn.Sequential(OrderedDict([\n",
    "    ('hidden_linear', nn.Linear(1, 8)),\n",
    "    ('hidden_activation', nn.Tanh()),\n",
    "    ('output_linear', nn.Linear(8, 1))\n",
    "]))\n",
    "\n",
    "seq_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hidden_linear.weight torch.Size([8, 1])\n",
      "hidden_linear.bias torch.Size([8])\n",
      "output_linear.weight torch.Size([1, 8])\n",
      "output_linear.bias torch.Size([1])\n"
     ]
    }
   ],
   "source": [
    "for name, param in seq_model.named_parameters():\n",
    "    print(name, param.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Parameter containing:\n",
       "tensor([-0.1786], requires_grad=True)"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "seq_model.output_linear.bias"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1, Training loss 142.5878, Validation loss 439.7242\n",
      "Epoch 1000, Training loss 4.9129, Validation loss 46.7108\n",
      "Epoch 2000, Training loss 3.4471, Validation loss 25.7202\n",
      "Epoch 3000, Training loss 2.8642, Validation loss 16.3709\n",
      "Epoch 4000, Training loss 2.5688, Validation loss 11.2405\n",
      "Epoch 5000, Training loss 2.3860, Validation loss 8.0017\n",
      "output tensor([[24.0087],\n",
      "        [ 7.7348]], grad_fn=<AddmmBackward>)\n",
      "answer tensor([[28.],\n",
      "        [ 8.]])\n",
      "hidden tensor([[ 0.0207],\n",
      "        [ 0.0272],\n",
      "        [-0.0065],\n",
      "        [ 0.1072],\n",
      "        [ 0.0060],\n",
      "        [ 0.0142],\n",
      "        [ 0.0004],\n",
      "        [-0.0004]])\n"
     ]
    }
   ],
   "source": [
    "optimizer = optim.SGD(seq_model.parameters(), lr=1e-3) # 为了稳定性调小了梯度\n",
    "\n",
    "training_loop(\n",
    "    n_epochs = 5000,\n",
    "    optimizer = optimizer,\n",
    "    model = seq_model,\n",
    "    loss_fn = nn.MSELoss(),\n",
    "    t_u_train = t_un_train,\n",
    "    t_u_val = t_un_val,\n",
    "    t_c_train = t_c_train,\n",
    "    t_c_val = t_c_val)\n",
    "\n",
    "print('output', seq_model(t_un_val))\n",
    "print('answer', t_c_val)\n",
    "print('hidden', seq_model.hidden_linear.weight.grad)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhoAAAFwCAYAAAD+PtKHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3Xl4nGW9//H3N9OkaZtk0i1NuqdlaUOLUKVSRUChWNQq\nuCDbEfCAigoHEcGiWZqyLxUFrMpSwIK4oGJFCoLwOwIeqpal+76lSZq2aZM0e2bu3x8zidM0aZNp\nJrN9Xtc1VzrP88zkezdt8sn93Is55xARERGJhJRoFyAiIiKJS0FDREREIkZBQ0RERCJGQUNEREQi\nRkFDREREIkZBQ0RERCJGQUNEREQiRkFDREREIkZBQ0RERCJGQUNEREQiRkFDREREImZAtAvoT2Zm\nwGigLtq1iIiIxKFMoNz1YqO0pAoaBEJGWbSLEBERiWNjgV09vTjZgkYdwM6dO8nKyop2LSIiInGj\ntraWcePGQS/vCiRb0AAgKytLQUNERKQfaDCoiIiIRIyChoiIiESMgoaIiIhEjIKGiIiIRIyChoiI\niERMUs46ERER6Smf37F8azVVdU3kZKYzM38YnhSLdllxQ0FDRESkG8tWVTB/6Roqapo6juV50yme\nW8CcaXlRrCx+6NaJiIhIF5atquDaJSsOCRkAlTVNXLtkBctWVUSpsviioCEiItKJz++Yv3QNXW3o\n0X5s/tI1+Pw93vIjaSloiIiIdLJ8a/VhPRmhHFBR08TyrdX9V1ScUtAQERHppKqu+5ARznXJTEFD\nRESkk5zM9D69LpkpaIiIiHQyM38Yed50upvEagRmn8zMH9afZcUlBQ0REZFOPClG8dwCgMPCRvvz\n4rkFWk+jBxQ0REREujBnWh6LLp9BrvfQ2yO53nQWXT5D62j0kDmXPFNzzCwLqKmpqSErKyva5YiI\nSBzQyqABtbW1eL1eAK9zrranr9PKoCIiIkfgSTFmTR4e7TLilm6diIiISMQoaIiIiEjEKGiIiIhI\nxChoiIiISMQoaIiIiEjEKGiIiIhIxChoiIiISMQoaIiIiEjEKGiIiIhIxChoiIiISMQoaIiIiEjE\nKGiIiIhIxChoiIiISMQoaIiIiEjEKGiIiIhIxMRE0DCzeWb2TzOrM7MqM/ujmZ3Y6ZonzMx1eiyL\nVs0iIiJydDERNICzgIeB04HZQCrwspkN6XTdMiAv5HFJfxYpIiIivTMg2gUAOOfmhD43syuBKuCD\n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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x11aba0d68>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "\n",
    "t_range = torch.arange(20., 90.).unsqueeze(1)\n",
    "\n",
    "fig = plt.figure(dpi=100)\n",
    "plt.xlabel(\"Fahrenheit\")\n",
    "plt.ylabel(\"Celsius\")\n",
    "plt.plot(t_u.numpy(), t_c.numpy(), 'o')\n",
    "plt.plot(t_range.numpy(), seq_model(0.1 * t_range).detach().numpy(), 'c-')\n",
    "plt.plot(t_u.numpy(), seq_model(0.1 * t_u).detach().numpy(), 'kx')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:py36]",
   "language": "python",
   "name": "conda-env-py36-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
